/**
 * This file is part of Adaptools.
 * 
 * Adaptools is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * Adaptools is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 * along with Adaptools.  If not, see http://www.gnu.org/licenses/. 
 */
package org.adaptools.repository.base;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Stack;

import org.adaptools.base.AdaptiveSymbols;
import org.adaptools.base.MechanismState;
import org.adaptools.base.Mechanism;
import org.adaptools.base.Queue;
import org.adaptools.base.Rule;
import org.adaptools.base.context.AdaptoolsContextFactory;
import org.adaptools.repository.util.UniqueIdentifier;
import org.apache.log4j.Logger;
import org.springframework.context.ApplicationContext;

/**
 * Class to implements {@link Mechanism} representing the data struct of system.
 * 
 * Note that this class have nothing business rule. It's just a data structure
 * 
 * @author Denys G. Santos (gsdenys@gmail.com)
 * @since 3.0.0
 */
public class MechanismRepository implements Mechanism, Serializable {
	private static final long serialVersionUID = 1230371096301235334L;

	private final static Logger LOG = Logger.getLogger(MechanismRepository.class);

	/**
	 * Unique Identifier to {@link Mechanism}
	 */
	private String uuid = null;

	/**
	 * {@link Collection} of {@link Rule} to store all {@link Mechanism}
	 * {@link Rule}
	 */
	private Collection<Rule> ruleCollection = new ArrayList<Rule>();

	/**
	 * Start state Name
	 */
	private String startState = null;

	/**
	 * Input {@link Queue}
	 */
	private Queue input = null;

	/**
	 * output {@link Queue}
	 */
	private Queue output = null;

	/**
	 * transducer {@link Queue}
	 */
	private Queue transducer = null;
	
	/**
	 * Rule state Source
	 */
	private String ruleStateSource = null;

	/**
	 * the machine State
	 */
	private MechanismState state;
	
	/**
	 * final State
	 */
	private Boolean finalState = Boolean.FALSE;
	
	
	private Stack<String> stack = null;
	
	
	/**
	 * Default Builder
	 */
	public MechanismRepository() {
		LOG.debug("Create a new Mechanism to the System.");

		ApplicationContext context = AdaptoolsContextFactory.getContext();

		this.input = (Queue) context.getBean("adaptools.base.queue");
		this.output = (Queue) context.getBean("adaptools.base.queue");
		this.transducer = (Queue) context.getBean("adaptools.base.queue");

		String uuid = UniqueIdentifier.generateUUID();
		this.uuid = uuid;
		
		this.stack = new Stack<String>();
	}

	/**
	 * @see org.adaptools.base.Mechanism#getId()
	 */
	@Override
	public String getId() {
		return this.uuid;
	}
	
	/**
	 * @see org.adaptools.base.Mechanism#setId(String)
	 */
	@Override
	public void setId(String UUID) {
		this.uuid = UUID;
	}

	/**
	 * @see org.adaptools.base.Mechanism#add(org.adaptools.base.Rule)
	 */
	@Override
	public void add(Rule rule) {
		if (rule == null) {
			LOG.error("Could not include nullable Rule");
		}

		if (this.startState == null) {
			String str = rule.getSource();
			if (str != null
					&& !str.startsWith(AdaptiveSymbols.ADAPTIVE_ADD_SYMBOL
							.getSimbol())
					&& !str.startsWith(AdaptiveSymbols.ADAPTIVE_REMOVE_SYMBOL
							.getSimbol())
					&& !str.startsWith(AdaptiveSymbols.ADAPTIVE_SEARCH_SYMBOL
							.getSimbol())) {

				LOG.debug("Setup the Start State: " + str);
				this.startState = str;
			}
		}

		this.ruleCollection.add(rule);
	}

	/**
	 * @see org.adaptools.base.Mechanism#add(java.util.Collection)
	 */
	@Override
	public void add(Collection<Rule> rules) {
		for (Rule rule : rules) {
			this.add(rule);
		}
	}

	/**
	 * @see org.adaptools.base.Mechanism#search(org.adaptools.base.Rule)
	 */
	@Override
	public Collection<Rule> search(Rule rule) {
		Collection<Rule> coll = new ArrayList<Rule>();

		// search rules
		for (Rule r : this.ruleCollection) {
			if (r.equals(rule)) {
				coll.add(r);
			}
		}

		return coll;
	}

	/**
	 * @see org.adaptools.base.Mechanism#remove(org.adaptools.base.Rule)
	 */
	@Override
	public void remove(Rule rule) {
		this.ruleCollection.remove(rule);
	}

	/**
	 * @see org.adaptools.base.Mechanism#remove(java.util.Collection)
	 */
	@Override
	public void remove(Collection<Rule> rules) {
		for (Rule rule : rules) {
			this.remove(rule);
		}
	}

	/**
	 * @see org.adaptools.base.Mechanism#getInitialStateName()
	 */
	@Override
	public String getInitialStateName() {
		return this.startState;
	}

	/**
	 * @see org.adaptools.base.Mechanism#getOutput()
	 */
	@Override
	public Queue getOutput() {
		return this.output;
	}

	/**
	 * @see org.adaptools.base.Mechanism#setOutput(org.adaptools.base.Queue)
	 */
	@Override
	public void setOutput(Queue output) {
		this.output = output;
	}

	/**
	 * @see org.adaptools.base.Mechanism#getQueue()
	 */
	@Override
	public Queue getQueue() {
		return this.input;
	}

	/**
	 * @see org.adaptools.base.Mechanism#setQueue(org.adaptools.base.Queue)
	 */
	@Override
	public void setQueue(Queue queue) {
		this.input = queue;
	}

	/**
	 * @see org.adaptools.base.Mechanism#getTransducer()
	 */
	@Override
	public Queue getTransducer() {
		return this.transducer;
	}

	/**
	 * @see org.adaptools.base.Mechanism#setTransducer(org.adaptools.base.Queue)
	 */
	@Override
	public void setTransducer(Queue transducer) {
		this.transducer = transducer;
	}

	/**
	 * @see org.adaptools.base.Mechanism#getRuleCollection()
	 */
	@Override
	public Collection<Rule> getRuleCollection() {
		return this.ruleCollection;
	}

	/**
	 * @see java.lang.Object#equals(java.lang.Object)
	 */
	@Override
	public boolean equals(Object obj) {
		if (this == obj) {
			return true;
		}
		if (obj == null) {
			return false;
		}
		if (!(obj instanceof MechanismRepository)) {
			return false;
		}
		MechanismRepository other = (MechanismRepository) obj;
		if (uuid == null) {
			if (other.uuid != null) {
				return false;
			}
		} else if (!uuid.equals(other.uuid)) {
			return false;
		}
		return true;
	}

	/**
	 * @see org.adaptools.base.Mechanism#getAdaptiveRules(String)
	 */
	@Override
	public Collection<Rule> getAdaptiveRules(String adaptativeRuleName) {
		Collection<Rule> collection = new ArrayList<Rule>();

		for (Rule rule : this.getRuleCollection()) {
			if (rule.isAdaptiveRule()) {
				if (rule.getHeader().substring(1).equals(adaptativeRuleName)) {
					collection.add(rule);
				}
			}
		}

		return collection;
	}

	/**
	 * @see org.adaptools.base.Mechanism#getAdaptiveRules()
	 */
	@Override
	public Collection<Rule> getAdaptiveRules() {
		Collection<Rule> collection = new ArrayList<Rule>();

		for (Rule rule : this.getRuleCollection()) {
			if (rule.isAdaptiveRule()) {
				collection.add(rule);
			}
		}

		return collection;
	}

	/**
	 * @see org.adaptools.base.Mechanism#getRuleState()
	 */
	@Override
	public String getRuleState() {
		return this.ruleStateSource;
	}

	/**
	 * @see org.adaptools.base.Mechanism#setRuleState(Rule)
	 */
	@Override
	public void setRuleState(String ruleState) {
		this.ruleStateSource = ruleState;
	}

	/**
	 * @see org.adaptools.base.Mechanism#setMachineState(MechanismState)
	 */
	@Override
	public void setMachineState(MechanismState state) {
		this.state = state;
		
	}

	/**
	 * @see org.adaptools.base.Mechanism#getMachineState()
	 */
	@Override
	public MechanismState getMachineState() {
		return this.state;
	}

	@Override
	public Mechanism copy() {
		Mechanism mechanism = new MechanismRepository();
		
		mechanism.setMachineState(this.getMachineState());
		mechanism.setOutput(this.output.copy());
		mechanism.setQueue(this.input.copy());
		mechanism.setTransducer(this.transducer.copy());
		mechanism.setRuleState(this.ruleStateSource);
		
		for (Rule rule : this.ruleCollection) {
			mechanism.add(rule.copy());
		}
		
		return mechanism;
	}

	/**
	 * @see org.adaptools.base.Mechanism#isFinalState()
	 */
	@Override
	public Boolean isFinalState() {
		return this.finalState;
	}

	/**
	 * @see org.adaptools.base.Mechanism#setFinalState(Boolean)
	 */
	@Override
	public void setFinalState(Boolean finalState) {
		this.finalState = finalState;
	}

	/**
	 * @see org.adaptools.base.Mechanism#getStack()
	 */
	@Override
	public Stack<String> getStack() {
		return this.stack;
	}

	/**
	 * @see org.adaptools.base.Mechanism#setStack(Stack)
	 */
	@Override
	public void setStack(Stack<String> stack) {
		this.stack = stack;
	}
	
	
}